package com.uinnova.product.eam.web.bm.peer;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.util.ListUtils;
import com.alibaba.excel.write.builder.ExcelWriterBuilder;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.binary.core.exception.BinaryException;
import com.binary.core.exception.MessageException;
import com.binary.core.util.BinaryUtils;
import com.uinnova.product.eam.base.exception.ServerException;
import com.uinnova.product.eam.base.util.CustomCellWriteHandler;
import com.uinnova.product.eam.base.util.EamUtil;
import com.uinnova.product.eam.model.asset.*;
import com.uinnova.product.eam.model.dto.DiagramContrastDataDto;
import com.uinnova.product.eam.service.ICISwitchSvc;
import com.uinnova.product.eam.service.es.IamsESCIDesignSvc;
import com.uinnova.product.eam.service.es.IamsESCIHistoryPrivateSvc;
import com.uinnova.product.eam.service.es.IamsESCIRltInfoHistoryDesignSvc;
import com.uinnova.product.eam.service.es.IamsESCIRltInfoHistoryPrivateSvc;
import com.uinnova.product.eam.service.impl.IamsCIRltSwitchSvc;
import com.uinnova.product.vmdb.comm.model.ci.CCcCiClass;
import com.uinnova.product.vmdb.comm.model.ci.CcCiAttrDef;
import com.uinnova.product.vmdb.comm.model.ci.CcCiClass;
import com.uinnova.product.vmdb.comm.util.CommUtil;
import com.uinnova.project.api.diagram.v2.client.ESDiagramApiClient;
import com.uinnova.project.base.diagram.comm.model.*;
import com.uinnova.project.db.diagram.VcDiagramVersionDao;
import com.uinnova.project.db.eam.ESDiagramDao;
import com.uinnova.project.db.eam.ESDiagramLinkDao;
import com.uinnova.project.db.eam.ESDiagramNodeDao;
import com.uinnova.project.service.eam.ESDiagramSvc;
import com.uino.api.client.permission.IUserApiSvc;
import com.uino.bean.cmdb.base.*;
import com.uino.bean.permission.base.SysUser;
import com.uino.bean.permission.query.CSysUser;
import com.uino.dao.cmdb.ESCIClassSvc;
import com.uino.dao.cmdb.ESRltClassSvc;
import com.uino.service.util.FileUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.io.ByteArrayOutputStream;
import java.net.URLEncoder;
import java.util.*;
import java.util.stream.Collectors;

@Service
@Slf4j
public class DiagramContrastPeer {
    @Resource
    ESDiagramApiClient diagramApiClient;

    @Resource
    ICISwitchSvc ciSwitchSvc;

    @Resource
    IamsCIRltSwitchSvc ciRltSwitchSvc;

    @Resource
    ESCIClassSvc esciClassSvc;

    @Resource
    ESRltClassSvc esRltClassSvc;

    @Resource
    ESDiagramDao esDiagramDao;

    @Resource
    ESDiagramNodeDao esDiagramNodeDao;

    @Resource
    ESDiagramLinkDao esDiagramLinkDao;

    @Resource
    IamsESCIHistoryPrivateSvc ciPrivateHistorySvc;

    @Resource
    IamsESCIDesignSvc ciDesignHistorySvc;

    @Resource
    IamsESCIRltInfoHistoryDesignSvc rltDesignHistorySvc;

    @Resource
    IamsESCIRltInfoHistoryPrivateSvc rltPrivateHistorySvc;

    @Resource
    ESDiagramSvc esDiagramSvc;

    @Resource
    VcDiagramVersionDao vcDiagramVersionDao;

    @Resource
    IUserApiSvc iUserApiSvc;


    public DiagramContrastVO diagramContras(DiagramContrastParam param) {
        /*
          1.查询视图信息
          2.获取视图中ci 及关系
          3.对比俩张图
         */
        DiagramContrastVO result = new DiagramContrastVO();
        // 源端视图获取信息
        ESDiagram firstDiagramInfo = queryDiagramInfo(param.getSourceDiagramId());
        DiagramContrastDataDto sourceCiAndRltInfo = queryCiAndRltInfoByDiagram(firstDiagramInfo, param.getSourceSheetId());
        // 目标端视图获取信息
        ESDiagram secondDiagramInfo = queryDiagramInfo(param.getTargetDiagramId());
        DiagramContrastDataDto targetCiAndRltInfo = queryCiAndRltInfoByDiagram(secondDiagramInfo, param.getTargetSheetId());

        List<CiContrastVO> ciContrastVOList = new ArrayList<>();
        result.setCiContrastVOList(ciContrastVOList);
        // 查询分类信息
        Set<Long> classIds = new HashSet<>();
        sourceCiAndRltInfo.getCiList().forEach(esciInfo -> classIds.add(esciInfo.getClassId()));
        targetCiAndRltInfo.getCiList().forEach(esciHistoryInfo -> classIds.add(esciHistoryInfo.getClassId()));

        CCcCiClass obj = new CCcCiClass();
        obj.setIds(classIds.toArray(new Long[0]));
        List<ESCIClassInfo> classInfoList = esciClassSvc.getListByCdt(obj);
        Map<Long, ESCIClassInfo> classInfoMap = classInfoList.stream().collect(Collectors.toMap(ESCIClassInfo::getId, e -> e, (k1, k2) -> k1));
        // 对比
        Map<String, String> sourceCiAndLocMap = sourceCiAndRltInfo.getCiAndLocMap();
        Map<String, String> targetCiAndLocMap = targetCiAndRltInfo.getCiAndLocMap();
        for (ESCIInfo sourceCiInfo : sourceCiAndRltInfo.getCiList()) {
            Map<String, ESCIInfo> ciMap = targetCiAndRltInfo.getCiMap();
            ESCIInfo targetCiInfo = ciMap.get(sourceCiInfo.getCiPrimaryKey());
            ESCIClassInfo esciClassInfo = classInfoMap.get(sourceCiInfo.getClassId());
            if (BinaryUtils.isEmpty(targetCiInfo)) {
                // 新增
                createAttrChange(ciContrastVOList, sourceCiInfo, esciClassInfo, null, 1, sourceCiAndLocMap.get(sourceCiInfo.getCiCode()), null);
            } else {
                // 对比属性
                Map<String, Object> attrs = sourceCiInfo.getAttrs();
                Map<String, Object> targetCiAttr = targetCiInfo.getAttrs();
                Map<String, ESCIAttrDefInfo> attrDefMap = Collections.emptyMap();
                if (!BinaryUtils.isEmpty(esciClassInfo)) {
                    List<ESCIAttrDefInfo> attrDefs = esciClassInfo.getAttrDefs();
                    attrDefMap = attrDefs.stream().collect(Collectors.toMap(CcCiAttrDef::getProStdName, e -> e, (k1, k2) -> k1));
                }
                List<AssetChangeAttrVO> changeAttrVOS = new ArrayList<>();
                contrastAttr(attrs, targetCiAttr, attrDefMap, changeAttrVOS);
                if (!CollectionUtils.isEmpty(changeAttrVOS)) {
                    createAttrChange(ciContrastVOList, sourceCiInfo, esciClassInfo, changeAttrVOS, 2, sourceCiAndLocMap.get(sourceCiInfo.getCiCode()), targetCiAndLocMap.get(targetCiInfo.getCiCode()));
                }
            }
        }

        for (ESCIInfo targetCiInfo : targetCiAndRltInfo.getCiList()) {
            if (!sourceCiAndRltInfo.getCiMap().containsKey(targetCiInfo.getCiPrimaryKey())) {
                // 删除
                ESCIClassInfo esciClassInfo = classInfoMap.get(targetCiInfo.getClassId());
                CiContrastVO ciContrastVO = new CiContrastVO();
                ciContrastVO.setOperate(3);
                ciContrastVO.setClassType(1);
                ciContrastVO.setClassInfo(esciClassInfo);
                ciContrastVO.setCiInfo(targetCiInfo);
                ciContrastVO.setName(BinaryUtils.isEmpty(targetCiInfo.getCiLabel()) ? "" : targetCiInfo.getCiLabel().replace("[", "").replace("]", "").replaceAll("\"", ""));
                ciContrastVO.setFirstKey(null);
                ciContrastVO.setSecondKey(targetCiAndLocMap.get(targetCiInfo.getCiCode()));
                ciContrastVOList.add(ciContrastVO);
            }
        }
        // 对比关系 -新增、删除
        contrastRlt(sourceCiAndRltInfo, targetCiAndRltInfo, ciContrastVOList);
        result.setFirstDiagramInfo(sourceCiAndRltInfo.getEsDiagramDTO());
        result.setSecondDiagramInfo(targetCiAndRltInfo.getEsDiagramDTO());
        return result;
    }

    /**
     *  根据视图信息 获取对应的图内 CI / RLT 信息
     *  数据获取逻辑：
     *      1.资产仓库视图 - 获取 CI / RLT 设计库历史库数据
     *      2.设计空间主视图 - 获取 CI / RLT 私有库数据
     *      3.设计空间快照/历史版本视图 - 获取 CI / RLT 私有库历史库数据
     * @param diagramInfo 视图信息
     * @param sheetId sheet页信息 为空是默认查询视图内所有
     * @return
     */
    private DiagramContrastDataDto queryCiAndRltInfoByDiagram(ESDiagram diagramInfo, String sheetId) {
        if (BinaryUtils.isEmpty(diagramInfo.getIsOpen()) || BinaryUtils.isEmpty(diagramInfo.getHistoryVersionFlag())) {
            throw new ServerException("根据图内标识获取数据异常");
        }
        DiagramContrastDataDto contrastData = new DiagramContrastDataDto();
        Boolean isDesign = diagramInfo.getIsOpen() == 0 ? Boolean.FALSE : Boolean.TRUE;
        Boolean isHistory = diagramInfo.getHistoryVersionFlag() == 1 ? Boolean.FALSE : Boolean.TRUE;

        // 获取视图 node / link 信息
        BoolQueryBuilder nodeBool = QueryBuilders.boolQuery();
        nodeBool.must(QueryBuilders.termQuery("dEnergy.keyword", diagramInfo.getDEnergy()));
        if (!BinaryUtils.isEmpty(sheetId)) {
            nodeBool.must(QueryBuilders.termQuery("sheetId.keyword", sheetId));
        }
        List<ESDiagramNode> nodeList = esDiagramNodeDao.getListByQuery(nodeBool);
        Map<String, String> ciAndLocMap = nodeList.stream().collect(Collectors.toMap(ESDiagramNode::getCiCode, ESDiagramNode::getKey, (k1, k2) -> k2));

        BoolQueryBuilder linkBool = QueryBuilders.boolQuery();
        linkBool.must(QueryBuilders.termQuery("dEnergy.keyword", diagramInfo.getDEnergy()));
        if (!BinaryUtils.isEmpty(sheetId)) {
            linkBool.must(QueryBuilders.termQuery("sheetId.keyword", sheetId));
        }
        List<ESDiagramLink> linkList = esDiagramLinkDao.getListByQuery(linkBool);
        Map<String, String> rltAndLocMap = linkList.stream().collect(Collectors.toMap(ESDiagramLink::getUniqueCode, ESDiagramLink::getKey, (k1, k2) -> k2));

        List<ESCIInfo> ciList = new ArrayList<>();
        List<ESCIRltInfo> rltList = new ArrayList<>();

        String type = "";
        // 视图为设计空间快照/历史数据 - 获取私有库历史库数据
        Map<String, ESDiagramNode> ciCodeNodeMap = new HashMap<>();
        List<String> nohasVersionNodeList = new ArrayList<>();
        for (ESDiagramNode node : nodeList) {
            String ciCode = node.getCiCode();
            Long version = node.getVersion();
            if (!BinaryUtils.isEmpty(ciCode)) {
                if (BinaryUtils.isEmpty(version)) {
                    // todo
                    nohasVersionNodeList.add(ciCode);
                } else {
                    ciCodeNodeMap.put(ciCode, node);
                }
            }
        }

        Map<String, ESDiagramLink> uniqueCodeLinkMap = new HashMap<>();
        List<String> nohasVersionLinkList = new ArrayList<>();
        for (ESDiagramLink link : linkList) {
            String uniqueCode = link.getUniqueCode();
            Long version = link.getVersion();
            if (!BinaryUtils.isEmpty(uniqueCode)) {
                if (BinaryUtils.isEmpty(version)) {
                    nohasVersionLinkList.add(uniqueCode);
                } else {
                    uniqueCodeLinkMap.put(uniqueCode, link);
                }
            }
        }
        if (isDesign) {
            // todo 视图为资产仓库数据 - 获取设计库历史库数据
            type = "";
            // 根据 version 和 ciCode 查询CI数据
            BoolQueryBuilder ciHistoryQuery = QueryBuilders.boolQuery();
            // 循环放入查询条件
            for (String nodeCode : ciCodeNodeMap.keySet()) {
                ESDiagramNode node = ciCodeNodeMap.get(nodeCode);
                BoolQueryBuilder oneQuery = QueryBuilders.boolQuery();
                oneQuery.must(QueryBuilders.termQuery("version", node.getVersion()));
                oneQuery.must(QueryBuilders.termQuery("ciCode.keyword", nodeCode));
                ciHistoryQuery.should(oneQuery);
            }
            ciList = ciDesignHistorySvc.getListByQuery(ciHistoryQuery);
            BoolQueryBuilder queryOrAttrs = QueryBuilders.boolQuery();
            for (String linkCode : uniqueCodeLinkMap.keySet()) {
                ESDiagramLink link = uniqueCodeLinkMap.get(linkCode);
                BoolQueryBuilder oneQuery = QueryBuilders.boolQuery();
                oneQuery.must(QueryBuilders.termQuery("version", link.getVersion()));
                oneQuery.must(QueryBuilders.termQuery("uniqueCode.keyword", linkCode));
                queryOrAttrs.should(oneQuery);
            }
            List<ESCIRltInfoHistory> rltDesignHistoryList = rltDesignHistorySvc.getListByQuery(queryOrAttrs);
            rltList = EamUtil.copy(rltDesignHistoryList, ESCIRltInfo.class);
        } else {
            if (isHistory) {
                type = "SNAPSHOT";
                if (!BinaryUtils.isEmpty(ciCodeNodeMap)) {
                    BoolQueryBuilder ciHistoryQuery = QueryBuilders.boolQuery();
                    for (String ciCode : ciCodeNodeMap.keySet()) {
                        ESDiagramNode node = ciCodeNodeMap.get(ciCode);
                        BoolQueryBuilder oneQuery = QueryBuilders.boolQuery();
                        oneQuery.must(QueryBuilders.termQuery("version", node.getVersion()));
                        oneQuery.must(QueryBuilders.termQuery("ciCode.keyword", ciCode));
                        oneQuery.must(QueryBuilders.termQuery("ownerCode.keyword", diagramInfo.getOwnerCode()));
                        ciHistoryQuery.should(oneQuery);
                    }
                    List<ESCIHistoryInfo> ciPrivateHistoryList = ciPrivateHistorySvc.getListByQuery(ciHistoryQuery);
                    ciList = EamUtil.copy(ciPrivateHistoryList, ESCIInfo.class);
                }

                if (!BinaryUtils.isEmpty(uniqueCodeLinkMap)) {
                    BoolQueryBuilder rltHistoryQuery = QueryBuilders.boolQuery();
                    for (String uniqueCode : uniqueCodeLinkMap.keySet()) {
                        ESDiagramLink link = uniqueCodeLinkMap.get(uniqueCode);
                        BoolQueryBuilder oneQuery = QueryBuilders.boolQuery();
                        oneQuery.must(QueryBuilders.termQuery("version", link.getVersion()));
                        oneQuery.must(QueryBuilders.termQuery("uniqueCode.keyword", uniqueCode));
                        oneQuery.must(QueryBuilders.termQuery("ownerCode.keyword", diagramInfo.getOwnerCode()));
                        rltHistoryQuery.should(oneQuery);
                    }
                    List<ESCIRltInfoHistory> rltPrivateHistoryList = rltPrivateHistorySvc.getListByQuery(rltHistoryQuery);
                    rltList = EamUtil.copy(rltPrivateHistoryList, ESCIRltInfo.class);
                }
            } else {
                type = "";
                // 视图为设计空间主视图 - 获取私有库数据
                List<String> privateCINode = nodeList.stream().filter(node -> !BinaryUtils.isEmpty(node.getCiCode())).
                        map(ESDiagramNode::getCiCode).collect(Collectors.toList());
                if (!CollectionUtils.isEmpty(privateCINode)) {
                    ciList = ciSwitchSvc.getCiByCodes(privateCINode, diagramInfo.getOwnerCode(), LibType.PRIVATE);
                }
                Set<String> privateRltLink = linkList.stream().filter(link -> !BinaryUtils.isEmpty(link.getUniqueCode())).
                        map(ESDiagramLink::getUniqueCode).collect(Collectors.toSet());
                if (!CollectionUtils.isEmpty(privateRltLink)) {
                    rltList = ciRltSwitchSvc.getRltByUniqueCodes(privateRltLink, diagramInfo.getOwnerCode(), LibType.PRIVATE);
                }
            }
        }
        List<ESDiagramDTO> diagramDTOS = esDiagramSvc.queryDiagramInfoByIds(new Long[]{diagramInfo.getId()}, type, Boolean.FALSE, Boolean.FALSE);
        if (BinaryUtils.isEmpty(diagramDTOS)) {
            throw new BinaryException("视图数据获取异常");
        }
        ESDiagramDTO esDiagramDTO = diagramDTOS.get(0);
        if (type.equals("SNAPSHOT")) {
            // 根据视图历史版本关联表视图创建人信息
            VcDiagramVersion vcDiagramVersion = vcDiagramVersionDao.selectById(esDiagramDTO.getDiagram().getId());
            String creator = vcDiagramVersion.getCreator();
            if (!BinaryUtils.isEmpty(creator)) {
                int indexOf = creator.lastIndexOf("[");
                if(indexOf != -1) {
                    creator = creator.substring(indexOf+1, creator.length()-1);
                }
                CSysUser cSysUser = new CSysUser();
                cSysUser.setLoginCodeEqual(creator);
                cSysUser.setDomainId(1L);
                cSysUser.setSuperUserFlags(new Integer[]{0,1});
                List<SysUser> sysUsers = iUserApiSvc.getSysUserByCdt(cSysUser);
                if (!BinaryUtils.isEmpty(sysUsers)) {
                    SysUser realUser = sysUsers.get(0);
                    esDiagramDTO.setCreator(realUser);
                    esDiagramDTO.getDiagram().setOwnerCode(realUser.getLoginCode());
                }
            }
        }
        contrastData.setEsDiagramDTO(esDiagramDTO);
        contrastData.setCiList(ciList);
        contrastData.setCiMap(ciList.stream().collect(Collectors.toMap(ESCIInfo::getCiPrimaryKey, ci->ci, (k1, k2)->k2)));
        contrastData.setCiAndLocMap(ciAndLocMap);
        contrastData.setRltAndLocMap(rltAndLocMap);
        contrastData.setRltList(rltList);
        return contrastData;
    }

    /**
     *  获取视图信息
     * @param diagramId
     * @return
     */
    private ESDiagram queryDiagramInfo(String diagramId) {
        List<ESDiagram> listByQuery = esDiagramDao.getListByQuery(QueryBuilders.termQuery("dEnergy.keyword", diagramId));
        if (BinaryUtils.isEmpty(listByQuery)) {
            throw new ServerException("视图标识查询异常：diagramId【" + diagramId + "】");
        } 
        return listByQuery.get(0);
    }

    private void contrastRlt(DiagramContrastDataDto sourceCiAndRltInfo, DiagramContrastDataDto targetCiAndRltInfo, List<CiContrastVO> ciContrastVOList) {
        List<ESCIRltInfo> sourceRltList = sourceCiAndRltInfo.getRltList();
        List<ESCIRltInfo> targetRltList = targetCiAndRltInfo.getRltList();
        List<ESCIInfo> sourceCiList = sourceCiAndRltInfo.getCiList();
        List<ESCIInfo> targetCiList = targetCiAndRltInfo.getCiList();
        Map<String, String> sourceRltCodeAndKeyMap = sourceCiAndRltInfo.getRltAndLocMap();
        Map<String, String> targetRltCodeAndKeyMap = targetCiAndRltInfo.getRltAndLocMap();

        Set<Long> rltClassIds = new HashSet<>();
        sourceRltList.forEach(e -> rltClassIds.add(e.getClassId()));
        targetRltList.forEach(e -> rltClassIds.add(e.getClassId()));
        Map<Long, ESCIClassInfo> rltClassMap = new HashMap<>();
        if (!CollectionUtils.isEmpty(rltClassIds)) {
            CCcCiClass cdt = new CCcCiClass();
            cdt.setIds(rltClassIds.toArray(new Long[0]));
            List<ESCIClassInfo> rltClassList = esRltClassSvc.getListByQuery(QueryBuilders.termsQuery("id", rltClassIds));
            rltClassMap = rltClassList.stream().collect(Collectors.toMap(CcCiClass::getId, e -> e, (k1, k2) -> k1));
        }

        Map<String, ESCIInfo> sourceCIMap = sourceCiList.stream().
                collect(Collectors.toMap(ESCIInfo::getCiCode, source -> source, (k1, k2) -> k1));
        Map<String, ESCIInfo> targetCiMap = targetCiList.stream().
                collect(Collectors.toMap(ESCIInfo::getCiCode, target -> target, (k1, k2) -> k1));

        // 拼装目标端map
        Map<String, ESCIRltInfo> targetRltMap = new HashMap<>();
        for (ESCIRltInfo targetRlt : targetRltList) {
            String rltSourceCiCode = targetRlt.getSourceCiCode();
            String rltTargetCiCode = targetRlt.getTargetCiCode();
            if (BinaryUtils.isEmpty(rltSourceCiCode) || BinaryUtils.isEmpty(rltTargetCiCode)) {
                log.error("########## 对比关系数据目标端标识拼装异常：【" + targetRlt.getId() + "】");
            } else {
                String key = targetCiMap.get(rltSourceCiCode).getCiPrimaryKey() + "-" + targetRlt.getClassId() + "-" + targetCiMap.get(rltTargetCiCode).getCiPrimaryKey();
                targetRltMap.put(key, targetRlt);
            }
        }

        for (ESCIRltInfo sourceRltInfo : sourceRltList) {
            ESCIClassInfo esciClassInfo = rltClassMap.get(sourceRltInfo.getClassId());
            String key = sourceCIMap.get(sourceRltInfo.getSourceCiCode()).getCiPrimaryKey() + "-"+sourceRltInfo.getClassId() + "-" + sourceCIMap.get(sourceRltInfo.getTargetCiCode()).getCiPrimaryKey();
            if (!targetRltMap.containsKey(key)) {
                CiContrastVO ciContrastVO = new CiContrastVO();
                ciContrastVO.setOperate(1);
                ciContrastVO.setClassType(2);
                ciContrastVO.setClassInfo(esciClassInfo);
                ciContrastVO.setRltInfo(sourceRltInfo);
                ciContrastVO.setFirstKey(sourceRltCodeAndKeyMap.get(sourceRltInfo.getUniqueCode()));
                ciContrastVO.setSecondKey(null);
                try {
                    String sourceCiSearchValues = sourceRltInfo.getSourceCiSearchValues();
                    String targetCiSearchValues = sourceRltInfo.getTargetCiSearchValues();
                    String[] sourceValues = sourceCiSearchValues.split("_");
                    String[] targetValues = targetCiSearchValues.split("_");
                    ciContrastVO.setName(sourceValues[sourceValues.length - 1] + "->" + targetValues[targetValues.length - 1]);
                } catch (Exception e) {
                    ciContrastVO.setName("-");
                    log.error("=====关系名称获取失败，关系数据信息 ：" + JSONObject.toJSONString(sourceRltInfo));
                }
                ciContrastVOList.add(ciContrastVO);
            } else {
                ESCIRltInfo esciRltInfoHistory = targetRltMap.get(key);
                Map<String, String> sourceAttrs = sourceRltInfo.getAttrs();
                Map<String, String> targetAttrs = esciRltInfoHistory.getAttrs();
                if (CollectionUtils.isEmpty(sourceAttrs) && CollectionUtils.isEmpty(targetAttrs)) {
                    continue;
                }
                Map<String, ESCIAttrDefInfo> attrDefMap = Collections.emptyMap();
                if (!BinaryUtils.isEmpty(esciClassInfo)) {
                    List<ESCIAttrDefInfo> attrDefs = esciClassInfo.getAttrDefs();
                    attrDefMap = attrDefs.stream().collect(Collectors.toMap(CcCiAttrDef::getProStdName, e -> e, (k1, k2) -> k1));
                }
                List<AssetChangeAttrVO> changeAttrVOS = new ArrayList<>();
                contrastRltAttr(sourceAttrs, targetAttrs, attrDefMap, changeAttrVOS);
                if (!CollectionUtils.isEmpty(changeAttrVOS)) {
                    CiContrastVO ciContrastVO = new CiContrastVO();
                    ciContrastVO.setOperate(2);
                    ciContrastVO.setClassType(2);
                    ciContrastVO.setClassInfo(esciClassInfo);
                    ciContrastVO.setRltInfo(sourceRltInfo);
                    ciContrastVO.setChangeAttr(changeAttrVOS);
                    ciContrastVO.setFirstKey(sourceRltCodeAndKeyMap.get(sourceRltInfo.getUniqueCode()));
                    ciContrastVO.setSecondKey(targetRltCodeAndKeyMap.get(esciRltInfoHistory.getUniqueCode()));
                    try {
                        String sourceCiSearchValues = sourceRltInfo.getSourceCiSearchValues();
                        String targetCiSearchValues = sourceRltInfo.getTargetCiSearchValues();
                        String[] sourceValues = sourceCiSearchValues.split("_");
                        String[] targetValues = targetCiSearchValues.split("_");
                        ciContrastVO.setName(sourceValues[sourceValues.length - 1] + "->" + targetValues[targetValues.length - 1]);
                    } catch (Exception e) {
                        ciContrastVO.setName("-");
                        log.error("=====关系名称获取失败，关系数据信息 ：" + JSONObject.toJSONString(sourceRltInfo));
                    }
                    ciContrastVOList.add(ciContrastVO);
                }

            }
        }
        Map<String, ESCIRltInfo> privateRltMap = sourceRltList.stream()
                .filter(e -> !BinaryUtils.isEmpty(sourceCIMap.get(e.getSourceCiCode())) && !BinaryUtils.isEmpty(sourceCIMap.get(e.getTargetCiCode())))
                .collect(Collectors.toMap(e -> sourceCIMap.get(e.getSourceCiCode()).getCiPrimaryKey() + "-" + e.getClassId() + "-" + sourceCIMap.get(e.getTargetCiCode()).getCiPrimaryKey(),
                        e -> e, (k1, k2) -> k1));
        for (ESCIRltInfo targetRlt : targetRltList) {
            String key = targetCiMap.get(targetRlt.getSourceCiCode()).getCiPrimaryKey()+"-"+targetRlt.getClassId()+"-"+targetCiMap.get(targetRlt.getTargetCiCode()).getCiPrimaryKey();
            if (!privateRltMap.containsKey(key)) {
                ESCIClassInfo esciClassInfo = rltClassMap.get(targetRlt.getClassId());
                CiContrastVO ciContrastVO = new CiContrastVO();
                ciContrastVO.setOperate(3);
                ciContrastVO.setClassType(2);
                ciContrastVO.setClassInfo(esciClassInfo);
                ciContrastVO.setRltInfo(targetRlt);
                ciContrastVO.setFirstKey(null);
                ciContrastVO.setSecondKey(targetRltCodeAndKeyMap.get(targetRlt.getUniqueCode()));
                try {
                    ESCIInfo sourceCiInRlt = targetCiMap.get(targetRlt.getSourceCiCode());
                    ESCIInfo targetCiInRlt = targetCiMap.get(targetRlt.getTargetCiCode());
                    List<String> sourceCiLabel = JSONArray.parseArray(sourceCiInRlt.getCiLabel(), String.class);
                    List<String> targetCiLabel = JSONArray.parseArray(targetCiInRlt.getCiLabel(), String.class);
                    ciContrastVO.setName(StringUtils.join(sourceCiLabel,",") + "->" + StringUtils.join(targetCiLabel,","));
                } catch (Exception e) {
                    ciContrastVO.setName("-");
                    log.error("=====关系名称获取失败，关系数据信息 ：" + JSONObject.toJSONString(targetRlt));
                }
                ciContrastVOList.add(ciContrastVO);
            }
        }
    }

    private void getDiagramCiAndRltInfo(ESDiagramDTO secondDiagramInfo, Map<String, Long> ciCodeVersionMap, Map<String, Long> rltUniqueCodeMap, Map<String, String> ciCodeMapKey, Map<String, String> rltCodeMapKey) {
        for (ESDiagramModel model : secondDiagramInfo.getDiagram().getModelList()) {
            if (!BinaryUtils.isEmpty(model.getNodeDataArray())) {
                model.getNodeDataArray().stream().forEach(e -> {
                    if (!BinaryUtils.isEmpty(e.getCiCode())&&!BinaryUtils.isEmpty(e.getKey())) {
                        ciCodeVersionMap.put(e.getCiCode(), e.getVersion());
                        ciCodeMapKey.put(e.getCiCode(), e.getKey());
                    }
                });
            }
            if (!BinaryUtils.isEmpty(model.getLinkDataArray())) {
                model.getLinkDataArray().stream().forEach(e->{
                    if (!BinaryUtils.isEmpty(e.getUniqueCode())&&!BinaryUtils.isEmpty(e.getKey())) {
                        rltUniqueCodeMap.put(e.getUniqueCode(), e.getVersion());
                        rltCodeMapKey.put(e.getUniqueCode(), e.getKey());
                    }
                });
            }
        }
    }

    private void contrastRltAttr(Map<String, String> attrs, Map<String, String> historyAttrs, Map<String, ESCIAttrDefInfo> attrDefMap, List<AssetChangeAttrVO> changeAttrVOS) {
        for (Map.Entry<String, String> entry : attrs.entrySet()) {
            String key = entry.getKey().trim().toUpperCase();
//                    if (IGNORE_ATTR.contains(key)) {
//                        designCiAttr.remove(key);
//                        continue;
//                    }
            AssetChangeAttrVO attrVO = new AssetChangeAttrVO();
            ESCIAttrDefInfo ccCiAttrDef = attrDefMap.get(key);
            if (!BinaryUtils.isEmpty(ccCiAttrDef)) {
                attrVO.setProType(ccCiAttrDef.getProType());
            }
            attrVO.setChangeAttr(key);
            String value = BinaryUtils.isEmpty(entry.getValue()) ? "" : entry.getValue();
            if (!historyAttrs.containsKey(key)) {
                // 删除属性
                attrVO.setAfterVal(BinaryUtils.isEmpty(value) ? "-" : value);
                attrVO.setBeforeVal("-");
                changeAttrVOS.add(attrVO);
                continue;
            }
            if (historyAttrs.containsKey(key) && !value.equals(historyAttrs.get(key))) {
                attrVO.setAfterVal(BinaryUtils.isEmpty(value) ? "-" : value);
                attrVO.setBeforeVal(BinaryUtils.isEmpty(historyAttrs.get(key)) ? "-" : historyAttrs.get(key));
                changeAttrVOS.add(attrVO);
                historyAttrs.remove(key);
                continue;
            }
            if (historyAttrs.containsKey(key) && value.equals(historyAttrs.get(key))) {
                historyAttrs.remove(key);
            }
        }
        for (Map.Entry<String, String> releaseAttr : historyAttrs.entrySet()) {
            if (BinaryUtils.isEmpty(releaseAttr
                    .getValue())) {
                continue;
            }
            AssetChangeAttrVO attrVO = new AssetChangeAttrVO();
            changeAttrVOS.add(attrVO);
            attrVO.setChangeAttr(releaseAttr.getKey());
            attrVO.setBeforeVal(BinaryUtils.isEmpty(releaseAttr.getValue()) ? "-" : releaseAttr.getValue());
            CcCiAttrDef ccCiAttrDef = attrDefMap.get(releaseAttr.getKey());
            if (!BinaryUtils.isEmpty(ccCiAttrDef)) {
                attrVO.setProType(ccCiAttrDef.getProType());
            }
        }

    }

    private void createAttrChange(List<CiContrastVO> ciContrastVOList, ESCIInfo esciInfo, ESCIClassInfo esciClassInfo, List<AssetChangeAttrVO> changeAttrVOS, Integer type,String firstKey,String secondKey) {
        CiContrastVO ciContrastVO = new CiContrastVO();
        ciContrastVO.setOperate(type);
        ciContrastVO.setCiInfo(esciInfo);
        ciContrastVO.setClassType(1);
        ciContrastVO.setClassInfo(esciClassInfo);
        ciContrastVO.setChangeAttr(changeAttrVOS);
        ciContrastVO.setName(BinaryUtils.isEmpty(esciInfo.getCiLabel()) ? "" : esciInfo.getCiLabel().replace("[", "").replace("]", "").replaceAll("\"", ""));
        ciContrastVO.setFirstKey(firstKey);
        ciContrastVO.setSecondKey(secondKey);
        ciContrastVOList.add(ciContrastVO);
    }

    private void contrastAttr(Map<String, Object> attrs, Map<String, Object> designCiAttr, Map<String, ESCIAttrDefInfo> attrDefMap, List<AssetChangeAttrVO> changeAttrVOS) {
        for (Map.Entry<String, Object> entry : attrs.entrySet()) {
            String key = entry.getKey().trim().toUpperCase();
//                    if (IGNORE_ATTR.contains(key)) {
//                        designCiAttr.remove(key);
//                        continue;
//                    }
            AssetChangeAttrVO attrVO = new AssetChangeAttrVO();
            ESCIAttrDefInfo ccCiAttrDef = attrDefMap.get(key);
            if (!BinaryUtils.isEmpty(ccCiAttrDef)) {
                attrVO.setProType(ccCiAttrDef.getProType());
            }
            attrVO.setChangeAttr(key);
            String value = BinaryUtils.isEmpty(entry.getValue()) ? "" : entry.getValue().toString();
            if (!designCiAttr.containsKey(key)) {
                // 删除属性
                attrVO.setAfterVal(BinaryUtils.isEmpty(value) ? "-" : value);
                changeAttrVOS.add(attrVO);
                continue;
            }
            String targetValue = BinaryUtils.isEmpty(designCiAttr.get(key)) ? "" : designCiAttr.get(key).toString();
            if (designCiAttr.containsKey(key) && !value.equals(targetValue)) {
                attrVO.setAfterVal(BinaryUtils.isEmpty(value) ? "-" : value);
                attrVO.setBeforeVal(BinaryUtils.isEmpty(targetValue) ? "-" : targetValue.toString());
                changeAttrVOS.add(attrVO);
                designCiAttr.remove(key);
                continue;
            }
            if (designCiAttr.containsKey(key) && value.equals(targetValue)) {
                designCiAttr.remove(key);
            }
        }
        for (Map.Entry<String, Object> releaseAttr : designCiAttr.entrySet()) {
            if (BinaryUtils.isEmpty(releaseAttr
                    .getValue())) {
                continue;
            }
            AssetChangeAttrVO attrVO = new AssetChangeAttrVO();
            changeAttrVOS.add(attrVO);
            attrVO.setChangeAttr(releaseAttr.getKey());
            attrVO.setBeforeVal(BinaryUtils.isEmpty(releaseAttr.getValue()) ? "" : releaseAttr.getValue().toString());
            CcCiAttrDef ccCiAttrDef = attrDefMap.get(releaseAttr.getKey());
            if (!BinaryUtils.isEmpty(ccCiAttrDef)) {
                attrVO.setProType(ccCiAttrDef.getProType());
            }
        }
    }

    private List<List<String>> head(DiagramContrastVO data) {
        List<List<String>> list = ListUtils.newArrayList();
        List<String> head0 = ListUtils.newArrayList();
        head0.add("对比结果");
        List<String> head1 = ListUtils.newArrayList();
        head1.add("类型");
        List<String> head2 = ListUtils.newArrayList();
        head2.add("名称");
        List<String> head3 = ListUtils.newArrayList();
        head3.add(data.getFirstDiagramInfo().getDiagram().getName());
        List<String> head4 = ListUtils.newArrayList();
        head4.add(data.getSecondDiagramInfo().getDiagram().getName()+"-基准");
        list.add(head0);
        list.add(head1);
        list.add(head2);
        list.add(head3);
        list.add(head4);
        return list;
    }

    private List<List<Object>> dataList(DiagramContrastVO diagramContrastVO) {

        List<List<Object>> list = ListUtils.newArrayList();
        for (CiContrastVO ciContrastVO : diagramContrastVO.getCiContrastVOList()) {
            List<Object> data = ListUtils.newArrayList();
            data.add(getOperateType(ciContrastVO.getOperate()));
            data.add(ciContrastVO.getClassInfo().getClassName());
            data.add(ciContrastVO.getName());
            if (ciContrastVO.getOperate().equals(1)) {
                data.add("新增");
            } else {
                data.add("");
            }
            if (ciContrastVO.getOperate().equals(3)) {
                data.add("删除");
            } else {
                data.add("");
            }
            list.add(data);
            if (!CollectionUtils.isEmpty(ciContrastVO.getChangeAttr())) {
                for (AssetChangeAttrVO assetChangeAttrVO : ciContrastVO.getChangeAttr()) {
                    List<Object> subData = ListUtils.newArrayList();
                    subData.add("");
                    subData.add("");
                    subData.add(assetChangeAttrVO.getChangeAttr());
                    subData.add(assetChangeAttrVO.getAfterVal());
                    subData.add(assetChangeAttrVO.getBeforeVal());
                    list.add(subData);
                }
            }
        }
        return list;
    }

    private Object getOperateType(Integer operate) {
        String operateStr = null;
        switch (operate) {
            case 1:
                operateStr = "新增";
                break;
            case 2:
                operateStr = "修改";
                break;
            case 3:
                operateStr = "删除";
                break;
            default:
                operateStr = "";
        }
        return operateStr;
    }


    public ResponseEntity<byte[]> export(DiagramContrastParam param) {
        DiagramContrastVO data = this.diagramContras(param);
        String fileName = FileUtil.ExcelUtil.getExportFileName("【" + data.getFirstDiagramInfo().getDiagram().getName() + "】-【"
                + data.getSecondDiagramInfo().getDiagram().getName() + "】视图对比", CommUtil.EXCEL07_XLSX_EXTENSION, true);
        ResponseEntity<byte[]> responseEntity = null;
        try (ByteArrayOutputStream out = new ByteArrayOutputStream()){
            ExcelWriterBuilder write = EasyExcel.write(out);
            ExcelWriter build = write.build();
            write.head(head(data)).sheet("视图对比").registerWriteHandler(new CustomCellWriteHandler()).doWrite(dataList(data));
//            write.head(head(data)).sheet("视图对比").doWrite(dataList(data));
            responseEntity = this.returnRes(out.toByteArray(), fileName);

            build.finish();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return responseEntity;
    }

    private ResponseEntity<byte[]> returnRes(byte[] bytes, String fileName) {
        HttpHeaders headers = new HttpHeaders();
        ResponseEntity<byte[]> entity = null;
        try {
            headers.add("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
            entity = new ResponseEntity<byte[]>(bytes, headers, HttpStatus.OK);
        } catch (Exception e) {
            throw new MessageException(e.getMessage());
        }
        return entity;
    }
}
